package pers.cz.util;

import org.apache.commons.io.filefilter.AndFileFilter;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.input.ReversedLinesFileReader;
import org.apache.commons.io.monitor.FileAlterationObserver;
import pers.cz.config.JefConfiguration;
import pers.cz.tools.StringUtils;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @program: PostGirl-panent
 * @description: 文件处理助手
 * @author: Cheng Zhi
 * @create: 2022-10-04 12:38
 **/
public class FileUtils {

    /**
     * 获取文件总行数
     * @param file
     * @return
     */
    public static long getFileLineCount(File file) {

        long lines= 0;
        try {
            //lines = Files.lines(Paths.get(file.getPath()), Charset.forName("utf-8")).count();
            FileReader fileReader = new FileReader(file);
            LineNumberReader lineNumberReader = new LineNumberReader(fileReader);
            lineNumberReader.skip(Long.MAX_VALUE);
            lines = lineNumberReader.getLineNumber() + 1;
            fileReader.close();
            lineNumberReader.close();
            return lines;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return lines;
    }

    /**
     * 获取文件当前写入数
     * @param file
     * @return
     */
    public static long getFilePointer(File file) {
        try {
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
            long filePointer = randomAccessFile.getFilePointer();
            return filePointer;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return 0;
    }
    /**
     * 读取指定行到末尾的内容
     * @param file
     * @param lines
     * @return
     */
    public static String readTailContent1(File file, long lines) {
        StringBuilder content =new StringBuilder();
        Stack<String> stack = new Stack<String>();
        try {
            // GB18030
            String chatset = JefConfiguration.get(LogConfigItem.LOG_FILE_CHATSET);
            ReversedLinesFileReader object = new ReversedLinesFileReader(file,Charset.forName(chatset));
            long counter = 0L;
            while(counter < lines) {
                // 对读取到的每一行进行压缩
                //String gunzip = StringCompressUtils.gzip(object.readLine() + '\n');
                //stack.push(gunzip);
                String ip = "";
                // 是否每行都添加主机ip
                if (JefConfiguration.getBoolean(LogConfigItem.LOG_ADD_ADDRESS, false)) {
                    ip = NetUtils.getLocalIp();
                    ip = ip + "|--|";
                }

                stack.push(ip + object.readLine() + "\r\n");
                counter ++;
            }
            long size = stack.size();
            for (long i=0; i<size; i++) {
                content.append(stack.pop());
                //content.append("'POST_GIRL'");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            stack = null;
        }
        return content.toString();
    }

    /**
     * 读取指定行到末尾的内容
     * @param file
     * @param lines
     * @return
     */
    public static String readTailContent(File file, long lines) {
        JefStringBuilder content =new JefStringBuilder();
        try {
            // GB18030
            String chatset = JefConfiguration.get(LogConfigItem.LOG_FILE_CHATSET,"UTF-8");
            ReversedLinesFileReader object = new ReversedLinesFileReader(file,Charset.forName(chatset));
            long counter = 0L;
            while(counter < lines) {
                // 对读取到的每一行进行压缩
                //String gunzip = StringCompressUtils.gzip(object.readLine() + '\n');
                //stack.push(gunzip);
                String ip = "";
                // 是否每行都添加主机ip
                if (JefConfiguration.getBoolean(LogConfigItem.LOG_ADD_ADDRESS, false)) {
                    ip = NetUtils.getLocalIp();
                    ip = ip + "|--|";
                }
                // 向前追加
                content.preAppand(ip + object.readLine() + "\r\n");
                counter ++;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return content.toString();
    }

    /**
     * 读取指定文件块
     * @param file
     * @param startPoint 起始位置
     * @param blockSize
     * @return
     */
    public static String readBlock(File file, long startPoint, int blockSize) {

        try {
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
            // 跳转到指定位置
            randomAccessFile.seek(startPoint);
            // 从指定位置读取指定大小的字节
            ByteBuffer byteBuffer = readByteBuffer(randomAccessFile, blockSize);
            // 关闭文件
            randomAccessFile.close();
            // 将字节数组转化为字符串
            String line = Charset.forName("utf-8").decode(byteBuffer).toString();
            return line;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return "";
    }

    /**
     * 读取文件中的指定大小文件块
     * @param randomAccessFile
     * @param byteCapacity
     * @return
     * @throws IOException
     */
    public static ByteBuffer readByteBuffer(RandomAccessFile randomAccessFile, int byteCapacity) throws IOException {

        // 读取指定的大小
        byte[] buff = new byte[byteCapacity];
        //Arrays.fill(buff, (byte) 0);
        int read = randomAccessFile.read(buff);
        // 将读取出的内容组织为ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(byteCapacity);
        // 判断一下，如果无内容则替换
        for (int i=0; i<read; i++) {
            buffer.put(buff[i]);
        }
        //buffer.put(buff);
        buffer.flip();
        return buffer;
    }


    private static ByteBuffer reAllocate(ByteBuffer stringBuffer) {
        final int capacity = stringBuffer.capacity();
        byte[] newBuffer = new byte[capacity * 2];
        System.arraycopy(stringBuffer.array(), 0, newBuffer, 0, capacity);
        return (ByteBuffer) ByteBuffer.wrap(newBuffer).position(capacity);
    }

    /**
     * 文件中追加内容
     * @param fileName
     * @param content
     */
    public static synchronized void writeFileByStreamByRandomAccess(String fileName, String content) {
        try {
            // 打开一个随机访问文件流，按读写方式
            RandomAccessFile randomFile = new RandomAccessFile(fileName, "rw");
            // 文件长度，字节数
            long fileLength = randomFile.length();
            long fileSize = fileLength;
            double size = Math.round(fileSize / 1024.0 / 1024.0 * 100) / 100.0;
            int splitSize = JefConfiguration.getInt(LogConfigItem.LOG_SPLIT_SIZE, 200);
            if (size >= splitSize) {
                updateFileName(fileName);
                randomFile = new RandomAccessFile(fileName, "rw");
                fileLength = randomFile.length();
            }
            // 将写文件指针移到文件尾。
            randomFile.seek(fileLength);
            randomFile.write(content.getBytes("utf-8"));
            randomFile.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 修改文件名
     * @param fileName
     */
    public static void updateFileName(String fileName) {

        String now = DateUtils.getCurrentDateStr();
        String newFileName = fileName + now;

        File target = new File(newFileName);
        if (target.exists()) { //新文件若存在，则删掉
            target.delete();
        }
        File file = new File(fileName); // 旧文件
        boolean result = file.renameTo(target); //将旧文件更名
        if(result) {
            System.out.println("更名成功");
        } else { //更名失败，则采取变相的更名方法
            try{
                Files.copy(file.toPath(), target.toPath());
                //FileUtils.copyFile(file, target); // 将旧文件拷贝给新文件
                System.out.println("更名成功222");
                //清空旧文件
                emptyFileContent(file);
            } catch (IOException e) {
                System.out.println("更名失败");
            }
        }
    }

    /**
     * 清空文件内容
     * @param file
     */
    public static void emptyFileContent(File file) {
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(file);
            out.write(new byte[0]);
        } catch (Exception e) {
            System.out.println("清空文件失败");
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static String decode(String str, String symbol) {

        String[] split = str.split(symbol);
        StringBuilder sb = new StringBuilder();
        try {
            for (String st : split) {
                sb.append(StringCompressUtils.gunzip(st));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    /**
     * 解析模糊字符串
     * @param fuzzyPath
     * @return
     */
    public static FileInfo analyzePath(String fuzzyPath) {

        // 将window路径转为/xx/xx/xx形式
        fuzzyPath = fuzzyPath.replaceAll("\\\\", "/");
        String fuzzyFileName = StringUtils.getSplitLast(fuzzyPath, "/");
        String filePath = StringUtils.getSplitNoLast(fuzzyPath, "/");
        // 记录替换前的长度
        int beforeReplaceSize = fuzzyFileName.length();
        String fileName = fuzzyFileName.replaceAll("\\*", "");
        int afterReplaceSize = fileName.length();
        FileInfo fileInfo = new FileInfo();
        fileInfo.setIsFuzzy(!(beforeReplaceSize == afterReplaceSize));
        fileInfo.setIsPreStr(fuzzyFileName.startsWith("\\*"));
        fileInfo.setIsFussixStr(fuzzyFileName.endsWith("\\*"));
        fileInfo.setIsSingleFile(beforeReplaceSize == afterReplaceSize);
        fileInfo.setFileName(fileName);
        fileInfo.setPathName(filePath);
        return fileInfo;
    }

    static class FileInfo {
        /**
         * 是否为***.txt类型
         */
        boolean isPreStr = false;

        /**
         * 去除*号后的文件名
         */
        String fileName;

        /**
         * 路径名
         */
        String pathName;

        /**
         * 是否带有*号
         */
        boolean isFuzzy = false;

        /**
         * 是否单纯的文件名不好含*
         */
        boolean isSingleFile = false;

        /**
         * 是否为cz****类型的文件
         */
        boolean isFussixStr = false;

        public boolean isSingleFile() {
            return isSingleFile;
        }

        public void setIsSingleFile(boolean singleFile) {
            isSingleFile = singleFile;
        }

        public boolean isFussixStr() {
            return isFussixStr;
        }

        public void setIsFussixStr(boolean fussixStr) {
            isFussixStr = fussixStr;
        }

        public boolean isPreStr() {
            return isPreStr;
        }

        public void setIsPreStr(boolean preStr) {
            isPreStr = preStr;
        }

        public String getFileName() {
            return fileName;
        }

        public void setFileName(String fileName) {
            this.fileName = fileName;
        }

        public String getPathName() {
            return pathName;
        }

        public void setPathName(String pathName) {
            this.pathName = pathName;
        }

        public boolean isFuzzy() {
            return isFuzzy;
        }

        public void setIsFuzzy(boolean fuzzy) {
            isFuzzy = fuzzy;
        }
    }

    public static List<IOFileFilter> getFileFilter(String fuzzyPath) {
        List<IOFileFilter> filters = new ArrayList<IOFileFilter>();
        filters.add(FileFilterUtils.fileFileFilter());
        FileInfo fileInfo = analyzePath(fuzzyPath);
        if (fileInfo.isFuzzy() && fileInfo.isPreStr()) {
            filters.add(FileFilterUtils.prefixFileFilter(fileInfo.getFileName()));
        } else if (fileInfo.isFuzzy() && fileInfo.isFussixStr()) {
            filters.add(FileFilterUtils.suffixFileFilter(fileInfo.getFileName()));
        } else if (fileInfo.isSingleFile()) {
            filters.add(FileFilterUtils.nameFileFilter(fileInfo.getFileName()));
        }

        return filters;
    }

    public static FileAlterationObserver getFileObServer(String fuzzyPath) {

        FileInfo fileInfo = analyzePath(fuzzyPath);
        String directory = fileInfo.getPathName();
        AndFileFilter andFileFilter = new AndFileFilter(getFileFilter(fuzzyPath));
        FileAlterationObserver observer = new FileAlterationObserver(directory, andFileFilter);

        return observer;
    }
}
